/***************************************************************************
            GenerateMethod.cpp  -  generate class with mock methods
                             -------------------
    begin                : Sun June 03 2007
    copyright            : (C) 2002-2008 by Ewald Arnold
    email                : mockpp@ewald-arnold.de

    $Id: GenerateMethod.cpp 1464 2008-10-28 22:47:28Z ewald-arnold $

 ***************************************************************************/

/**************************************************************************
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 ***************************************************************************/

#include "GenerateMethod.h"

#include <iostream>

#ifdef _MSC_VER
#  include <mockpp/mockpp_config-msvc_71.h>

#elif defined(__BORLANDC__)
# include <mockpp/mockpp_config-bcb5.h>

#elif defined(__CYGWIN__)
# ifdef HAVE_CONFIG_H
#  include <config.h>
# else
#  include <mockpp/mockpp_config.h>
# endif
#else

# ifdef HAVE_CONFIG_H
#  include <config.h>
# else
#  include <mockpp/mockpp_config.h>
# endif
#endif

GenerateMethod::~GenerateMethod()
{
}


void GenerateMethod::generateHeaderHead(std::ostream & h_file,
                                        const std::string &name,
                                        const std::string &methname,
                                        const MockppIdlClass &theClass)
{
  const std::vector<unsigned> argscnt = theClass.getArgsCount();

  h_file << "#ifndef " << name + "Mock_H\n"
         << "#define " << name + "Mock_H\n"
         << "\n"
           << "///////////////////////////////////////////////////////////////////\n"
           << "// Automatically generated by xml2mockpp v" << MOCKPP_VERSION << ".\n"
           << "// xml2mockpp is part of Mock Objects for C++.\n"
           << "// See also http://mockpp.sourceforge.net\n"
           << "//\n"
           << "// Don't edit manually unless you know what you are doing\n"
           << "///////////////////////////////////////////////////////////////////\n"
         << "\n\n";

  h_file << "#include <mockpp/mockpp.h> // always first\n\n";

  h_file << "#define MOCKPP_GENERATED_HPP 1\n\n";

  std::string methname_lower;
  for (unsigned i = 0; i < methname.length(); ++i)
    methname_lower += /*std::*/ tolower(methname[i]);

  h_file << "#include <mockpp/" << methname_lower << "ing/" << methname << "ableMockMethod.h>\n";
  for (unsigned i = 0; i < argscnt.size(); ++i)
    h_file << "#include <mockpp/" << methname_lower << "ing/" << methname << "ableMockMethod" << argscnt[i] << ".h>\n";

  h_file << "\n#include \"" << theClass.getSource() << "\"\n";

  h_file << "\n\n"
         << "class " << name << "Mock\n"
         << "  : public MOCKPP_NS::" << methname << "ableMockObject\n"
         << "  , public " << name << "\n"
         << "{\n"
         << "  public:\n\n";

  for(unsigned ictor = 0; ictor < theClass.numCtors(); ++ictor)
  {
    h_file << "    " << name << "Mock(";
    Method ctor = theClass.getCtor(ictor);
    for (unsigned iarg = 0; iarg < ctor.numArgs(); ++iarg)
    {
      h_file << ctor.getArg(iarg).getCppString(0)
             << ", ";
    }

    h_file << "const MOCKPP_NS::String &name = MOCKPP_PCHAR(\"" << name << "Mock\"), MOCKPP_NS::VerifiableList *parent = 0);\n\n";
  }
}


void GenerateMethod::generateHeaderMocker(std::ostream & h_file,
                                          const std::string &methname,
                                          const MockppIdlClass &theClass)
{
  for (unsigned i = 0; i < theClass.numMethods(); ++i)
  {
    Method method = theClass.getMethod(i);
    h_file << "    MOCKPP_NS::" + methname + "ableMockMethod" << method.numArgs()
            << "<" << method.getTemplateSignature() << "> "
            << method.getOverloadName() << "_mocker;\n";
  }
}


void GenerateMethod::generateHeaderMethods(std::ostream & h_file,
                                           const MockppIdlClass &theClass)
{
  for (unsigned i = 0; i < theClass.numMethods(); ++i)
  {
    const Method method = theClass.getMethod(i);

    if (!method.isVirtual())
    {
      std::cerr << "Warning: method in base class not 'virtual': "
                << method.getCppString(0, true, "Mock")
                << std::endl;
      h_file << "    // WARNING: method in base class is not virtual and is therefore *hidden*\n";
    }


    if (method.getName() != method.getOverloadName())
      h_file << "    // mapped to " << method.getOverloadName() << "_mocker (there are overloaded methods)\n";
    else
      h_file << "    // mapped to " << method.getName() << "_mocker\n";

    h_file << "    " << method.getCppString(0, false, "") << "\n\n";
  }
}



void GenerateMethod::generateHeaderReferenceVariables(std::ostream & h_file,
                                                      const MockppIdlClass &theClass)
{
  h_file << "\n  private:\n\n";
  for (unsigned i = 0; i < theClass.numMethods(); ++i)
  {
    const Method method = theClass.getMethod(i);
    const bool reftype = method.getType().isReference();
    if (reftype)
      h_file << "    mutable " << method.getType().getName() << " mockpp_refFor_" << method.getOverloadName() << ";\n";
  }
}


void GenerateMethod::generateHeaderTail(std::ostream & h_file,
                                        const std::string &name)
{
  h_file << "};\n\n";
  h_file << "#endif // " << name + "Mock_H\n\n";
}


void GenerateMethod::generateSourceHead(std::ostream & cpp_file,
                                        const std::string &h_name,
                                        const MockppIdlClass &theClass)
{
  cpp_file<< "///////////////////////////////////////////////////////////////////\n"
           << "// Automatically generated by xml2mockpp v" << MOCKPP_VERSION << ".\n"
           << "// xml2mockpp is part of Mock Objects for C++.\n"
           << "// See also http://mockpp.sourceforge.net\n"
           << "//\n"
           << "// Don't edit manually unless you know what you are doing\n"
           << "///////////////////////////////////////////////////////////////////\n"
           << "\n";

  cpp_file << "#include <mockpp/mockpp.h> // always first\n\n";

  cpp_file << "#define MOCKPP_GENERATED_CPP 1\n\n";

  cpp_file << "#define MOCKPP_ENABLE_DEFAULT_FORMATTER\n";
  cpp_file << "#include <mockpp/compat/Formatter.h>\n\n";

  cpp_file << "#include \"" << h_name << "\"\n\n\n";
}


void GenerateMethod::generateUserSourceHead(std::ostream & cpp_file,
                                            const std::string &h_name)
{
  cpp_file << "\n"
           << "/////////////////////////////////////////////////\n"
           << "// Automatically generated by xml2mockpp v" << MOCKPP_VERSION << ".\n"
           << "// xml2mockpp is part of Mock Objects for C++.\n"
           << "// See also http://mockpp.sourceforge.net\n"
           << "//\n"
           << "// Intended for manual and persistent changes \n"
           << "/////////////////////////////////////////////////\n"
           << "\n";

  cpp_file << "#include \"" << h_name << "\"\n\n";
}


void GenerateMethod::generateSourceCtors(std::ostream & cpp_file,
                                         const std::string &name,
                                         const std::string &methname,
                                         const MockppIdlClass &theClass)
{
  for(unsigned ictor = 0; ictor < theClass.numCtors(); ++ictor)
  {
    cpp_file << name << "Mock::" << name << "Mock(";
    Method ctor = theClass.getCtor(ictor);

    for (unsigned iarg = 0; iarg < ctor.numArgs(); ++iarg)
      cpp_file << ctor.getArg(iarg).getCppString(0) << ", ";
    cpp_file << "const MOCKPP_NS::String &name, MOCKPP_NS::VerifiableList *parent)\n";


    std::string inherited;
    for (unsigned iarg = 0; iarg < ctor.numArgs(); ++iarg)
    {
      if (iarg != 0)
        inherited += ", ";
      inherited += ctor.getArg(iarg).getName();
    }

    cpp_file << "  : MOCKPP_NS::"  << methname << "ableMockObject("
              << "name, parent)\n"
              << "  , " << name << "("
              << inherited
              << ")\n";

    for (unsigned iovr = 0; iovr < theClass.numMethods(); ++iovr)
    {
      Method method = theClass.getMethod(iovr);
      cpp_file << "  , " << method.getOverloadName()
                << "_mocker(MOCKPP_PCHAR(\""
                << theClass.getName() + "Mock::" + method.getOverloadName()
                << "\"), this)\n";
    }

    cpp_file << "{\n"
                "}\n\n\n";
  }
}


void GenerateMethod::generateSourceMethods(std::ostream & cpp_file,
                                           const MockppIdlClass &theClass)
{
  for (unsigned i = 0; i < theClass.numMethods(); ++i)
  {
    const Method method = theClass.getMethod(i);
    cpp_file << method.getCppString(0, true, "Mock") << "\n"
             << "{\n";

    const bool reftype = method.getType().isReference();
    if (reftype)
    {
      cpp_file << "  mockpp_refFor_"
               << method.getOverloadName() << " = ";
    }

    else if (method.getType().getName() != "void" || method.getType().getLeft() != "" || method.getType().getRight() != "")
      cpp_file << "  return ";

    else
      cpp_file << "  ";

    cpp_file << method.getOverloadName() << "_mocker.forward(";

    for (unsigned iarg = 0; iarg < method.numArgs(); ++iarg)
    {
      if (iarg != 0)
        cpp_file << ", ";
//      cpp_file << method.getArg(iarg).getConstCastedName();

      char buffer[40];
      std::sprintf(buffer, "%d", iarg);
      std::string s = std::string("p") + buffer;
      cpp_file << Argument::getConstCastedName(method.getArg(iarg).getName(),
                                                method.getArg(iarg).getType(),
                                                "",
                                                "& ");
    }


    cpp_file << ");\n";

    if (reftype)
      cpp_file << "  return mockpp_refFor_"
               << method.getOverloadName() << ";\n";

    cpp_file << "}\n\n\n";
  }
}

